package main

import (
	"context"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sort"
	"sync"
	"syscall"
	"time"

	"github.com/goburrow/modbus"
)

type DeviceStatus struct {
	Address            byte
	Online             bool
	LastSuccess        time.Time
	LastError          error
	ConsecutiveFailure int
	LatestRegisters    []uint16
	LatestRaw          []byte
}

var (
	statusMap = make(map[byte]*DeviceStatus)
	statusMu  sync.RWMutex

	// 串口访问互斥
	handlerMu sync.Mutex
)

func main() {
	// 串口配置
	port := "COM5" // 按实际使用情况修改调整
	baudRate := 9600
	dataBits := 8
	parity := "N"
	stopBits := 1
	timeout := 3 * time.Second

	// 设备地址列表
	addresses := []byte{1, 2, 3, 4}
	for _, addr := range addresses {
		statusMap[addr] = &DeviceStatus{Address: addr}
	}

	// 打开串口
	handler := modbus.NewRTUClientHandler(port)
	handler.BaudRate = baudRate
	handler.DataBits = dataBits
	handler.Parity = parity
	handler.StopBits = stopBits
	handler.Timeout = timeout
	handler.SlaveId = addresses[0]

	if err := handler.Connect(); err != nil {
		log.Fatalf("串口连接失败: %v", err)
	}
	defer handler.Close()

	client := modbus.NewClient(handler)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 捕获退出信号
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
		<-c
		fmt.Println("\n收到退出信号，准备退出...")
		cancel()
	}()

	// 启动顺序轮询（核心修改点）
	go sequentialPoll(ctx, client, handler, addresses, 3*time.Second)

	// 启动 HTTP 服务
	go startHTTPServer()

	// 定期打印汇总（可调整周期）
	go func() {
		ticker := time.NewTicker(30 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ctx.Done():
				return
			case <-ticker.C:
				printSummary()
			}
		}
	}()

	<-ctx.Done()
	printSummary()
	fmt.Println("程序结束。")
}

// 顺序轮询：1 -> 2 -> 3 -> 4 -> 再回到 1，如此循环
// interval 表示读取一个设备后到开始下一个设备之间等待的时间（不剔除读取耗时）
func sequentialPoll(ctx context.Context, client modbus.Client, handler *modbus.RTUClientHandler, addresses []byte, interval time.Duration) {
	log.Printf("开始顺序轮询，设备列表=%v，间隔=%s", addresses, interval)
	for {
		for _, addr := range addresses {
			select {
			case <-ctx.Done():
				return
			default:
			}
			start := time.Now()
			raw, err := readDevice(client, handler, addr, 0, 10)
			if err != nil {
				statusMu.Lock()
				ds := statusMap[addr]
				ds.ConsecutiveFailure++
				ds.Online = false
				ds.LastError = err
				statusMu.Unlock()

				log.Printf("[READ] addr=%d error=%v ts=%s duration=%s",
					addr, err, start.Format(time.RFC3339), time.Since(start).Truncate(time.Millisecond))
			} else {
				regs := parseUint16Registers(raw)

				statusMu.Lock()
				ds := statusMap[addr]
				ds.LatestRegisters = regs
				ds.LastSuccess = time.Now()
				ds.ConsecutiveFailure = 0
				ds.Online = true
				ds.LastError = nil
				ds.LatestRaw = raw
				statusMu.Unlock()

				log.Printf("[READ] addr=%d ok bytes=%dB hex=%s regs=%v ts=%s duration=%s",
					addr,
					len(raw),
					formatHexBytes(raw),
					regs,
					start.Format(time.RFC3339),
					time.Since(start).Truncate(time.Millisecond),
				)
			}

			// 每个设备之间的间隔
			select {
			case <-ctx.Done():
				return
			case <-time.After(interval):
			}
		}
	}
}

// 串行访问读取
func readDevice(client modbus.Client, handler *modbus.RTUClientHandler, slaveID byte, start uint16, quantity uint16) ([]byte, error) {
	handlerMu.Lock()
	defer handlerMu.Unlock()

	handler.SlaveId = slaveID
	results, err := client.ReadHoldingRegisters(start, quantity)
	if err != nil {
		return nil, fmt.Errorf("读取寄存器失败: %w", err)
	}
	return results, nil
}

// 解析寄存器
func parseUint16Registers(b []byte) []uint16 {
	out := make([]uint16, 0, len(b)/2)
	for i := 0; i+1 < len(b); i += 2 {
		val := binary.BigEndian.Uint16(b[i : i+2])
		out = append(out, val)
	}
	return out
}

// HEX 格式化
func formatHexBytes(b []byte) string {
	if len(b) == 0 {
		return "-"
	}
	const hexDigits = "0123456789ABCDEF"
	out := make([]byte, 0, len(b)*3)
	for i, v := range b {
		if i > 0 {
			out = append(out, ' ')
		}
		out = append(out, hexDigits[v>>4], hexDigits[v&0x0F])
	}
	return string(out)
}

// HTTP 服务
func startHTTPServer() {
	mux := http.NewServeMux()

	// 根路径跳转到静态 index.html
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/" {
			http.Redirect(w, r, "/static/index.html", http.StatusFound)
			return
		}
		http.NotFound(w, r)
	})

	// 静态文件目录：./static
	fileServer := http.FileServer(http.Dir("./static"))
	mux.Handle("/static/", http.StripPrefix("/static/", fileServer))

	// JSON 状态接口
	mux.HandleFunc("/api/status", handleJSONStatus)

	server := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}
	log.Println("HTTP 服务已启动: http://localhost:8080/  (静态目录: ./static)")
	if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		log.Printf("HTTP 服务错误: %v", err)
	}
}

// JSON 接口
func handleJSONStatus(w http.ResponseWriter, r *http.Request) {
	type js struct {
		Address            byte     `json:"address"`
		Online             bool     `json:"online"`
		LastSuccess        string   `json:"last_success"`
		ConsecutiveFailure int      `json:"consecutive_failure"`
		LastError          string   `json:"last_error,omitempty"`
		LatestRegisters    []uint16 `json:"latest_registers"`
		LatestRawHex       string   `json:"latest_raw_hex"`
	}

	statusMu.RLock()
	addresses := make([]int, 0, len(statusMap))
	for addr := range statusMap {
		addresses = append(addresses, int(addr))
	}
	sort.Ints(addresses)

	resp := make([]js, 0, len(addresses))
	for _, a := range addresses {
		ds := statusMap[byte(a)]
		item := js{
			Address:            ds.Address,
			Online:             ds.Online,
			LastSuccess:        formatTimeOrDash(ds.LastSuccess),
			ConsecutiveFailure: ds.ConsecutiveFailure,
			LatestRegisters:    ds.LatestRegisters,
			LatestRawHex:       formatHexBytes(ds.LatestRaw),
		}
		if ds.LastError != nil {
			item.LastError = ds.LastError.Error()
		}
		resp = append(resp, item)
	}
	statusMu.RUnlock()

	writeJSON(w, resp)
}

func writeJSON(w http.ResponseWriter, v interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	enc := json.NewEncoder(w)
	enc.SetIndent("", "  ")
	if err := enc.Encode(v); err != nil {
		http.Error(w, "JSON 编码失败: "+err.Error(), http.StatusInternalServerError)
	}
}

// 汇总打印
func printSummary() {
	statusMu.RLock()
	addresses := make([]int, 0, len(statusMap))
	for addr := range statusMap {
		addresses = append(addresses, int(addr))
	}
	sort.Ints(addresses)
	fmt.Println("------ 设备在线状态汇总 ------")
	for _, a := range addresses {
		ds := statusMap[byte(a)]
		if ds.Online {
			fmt.Printf("设备 %d: 在线 | 最近成功: %s | 连续失败: %d | 寄存器: %v | HEX: %s\n",
				ds.Address,
				ds.LastSuccess.Format(time.RFC3339),
				ds.ConsecutiveFailure,
				ds.LatestRegisters,
				formatHexBytes(ds.LatestRaw))
		} else {
			fmt.Printf("设备 %d: 离线 | 最近成功: %s | 连续失败: %d | 错误: %v\n",
				ds.Address,
				formatTimeOrDash(ds.LastSuccess),
				ds.ConsecutiveFailure,
				ds.LastError)
		}
	}
	statusMu.RUnlock()
	fmt.Println("--------------------------------")
}

func formatTimeOrDash(t time.Time) string {
	if t.IsZero() {
		return "-"
	}
	return t.Format(time.RFC3339)
}
